home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / sound / audioconverter / audioconvert.c < prev    next >
Encoding:
Text File  |  2000-06-23  |  37.4 KB  |  1,191 lines

  1. //////////
  2. //
  3. //    File:        AudioConvert.c
  4. //
  5. //    Contains:    Sample code showing how to use the Sound Manager's SoundConverter routines. 
  6. //
  7. //    Written by:    Jim Reekes
  8. //    Revised by: Tim Monroe
  9. //
  10. //    Copyright:    © 1999 by Apple Computer, Inc., all rights reserved.
  11. //
  12. //    Change History (most recent first):
  13. //
  14. //       <4>         07/13/99    rtm        final adjustments; fixed cross-platform problems resulting from using
  15. //                                    kSoundNotCompressed ('NONE') format (thanks to EJ Campbell!); see NOTE below
  16. //       <3>         07/07/99    rtm        further work; revised coding style; added some comments
  17. //       <2>         03/12/99    rtm        added (theDialog == NULL) check to AudConv_SFGetDialogHook to prevent
  18. //                                    crashing on Windows; simplified UPP handling
  19. //       <1>         03/09/99    rtm        first file from jr; added some comments; added Windows-specific code
  20. //                                    and endian macros
  21. //
  22. //    This code shows how to use the SoundConverter routines (introduced in Sound Manager version 3.2). It
  23. //    provides routines that convert an AIFF or AIFF-C file into either a QuickTime movie or into an AIFF or
  24. //    AIFF-C file. The user can select the desired target format and settings in a dialog box displayed by
  25. //    a call to SCRequestImageSettings.
  26. //
  27. //    You should note in particular that this sample code shows how to handle the compression parameters chunk
  28. //    that might be present in an AIFF or AIFF-C file. The compression parameters chunk contains parameters
  29. //    that are required to decompress the audio data in the file.
  30. //
  31. //    NOTE: Do not use kSoundNotCompressed as an output format for multi-byte audio data if you want to create
  32. //    movies that are to be shared across platforms. QuickTime needs to know the endianness of the sound data,
  33. //    and kSoundNotCompressed does not indicate whether the data is big- or little-endian. Since in this sample
  34. //    code we are reading our data from an AIFF file, we specify that any non-compressed multi-byte data is of
  35. //    the format k16BitBigEndianFormat. 
  36. //
  37. //////////
  38.  
  39. #include "AudioConvert.h"
  40.  
  41.  
  42. //////////
  43. //
  44. // main/WinMain 
  45. // The main function for this application.
  46. //
  47. //////////
  48.  
  49. #if TARGET_OS_MAC
  50. void main (void)
  51. #elif TARGET_OS_WIN32
  52. int CALLBACK WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR theCmdLine, int nCmdShow)
  53. #endif
  54. {
  55.     SoundComponentData        myDestInfo;
  56.     OSType                    myTypeList[2] = {kQTFileTypeAIFF, kQTFileTypeAIFC};
  57.     StandardFileReply        myReply;
  58.     FSSpec                    myInputFSSpec;
  59.     Boolean                    myOutputAIFF;
  60.     OSErr                    myErr = noErr;
  61.  
  62. #if TARGET_OS_WIN32
  63.     char                    myFileName[MAX_PATH];
  64.     DWORD                    myLength;
  65.     short                     myAppResFile = -1;                    // file reference number for this application's resource file
  66.     
  67.     InitializeQTML(0L);                                            // initialize QuickTime Media Layer
  68. #endif    
  69.  
  70. #if TARGET_OS_MAC
  71.     MaxApplZone();                                                // init everything
  72.     InitGraf(&qd.thePort);
  73.     InitFonts();
  74.     FlushEvents(everyEvent,0);
  75.     InitWindows();
  76.     InitMenus();
  77.     InitDialogs(NULL);
  78.     TEInit();
  79.     InitCursor();
  80. #endif    
  81.     
  82.     myErr = EnterMovies();
  83.     FailIf(myErr != noErr, Exit);
  84.  
  85. #if TARGET_OS_WIN32
  86.     // open the application's resource file, if it exists
  87.     myLength = GetModuleFileName(NULL, myFileName, MAX_PATH);    // NULL means: the current process
  88.     if (myLength != 0) {
  89.         FSSpec                myFSSpec;
  90.         
  91.         NativePathNameToFSSpec(myFileName, &myFSSpec, 0L);
  92.  
  93.         myAppResFile = FSpOpenResFile(&myFSSpec, fsRdWrPerm);
  94.         if (myAppResFile != -1)
  95.             UseResFile(myAppResFile);
  96.     }
  97. #endif    
  98.  
  99.     StandardGetFile(NULL, 2, myTypeList, &myReply);
  100.     if (myReply.sfGood) {
  101.         DlgHookYDUPP        myDlgHookUPP = NULL;
  102.         Point                myTopLeft;
  103.         
  104.         myDlgHookUPP = NewDlgHookYDProc(AudConv_SFGetDialogHook);
  105.         
  106.         myInputFSSpec = myReply.sfFile;                            // save input file spec
  107.  
  108.         myTopLeft.v = -1;                                        // -1,-1 means to center the dialog
  109.         myTopLeft.h = -1;
  110.         myOutputAIFF = true;                                    // init default to AIFF output
  111.         CustomPutFile(NULL, c2pstr(kAIFFFileSaveName), &myReply, rCustomGetFileDialog, myTopLeft, myDlgHookUPP, NULL, NULL, NULL, &myOutputAIFF);
  112.         if (myReply.sfGood) {
  113.             if (myReply.sfReplacing) {
  114.                 myErr = FSpDelete(&myReply.sfFile);
  115.                 FailIf(myErr != noErr, Failure);
  116.             }
  117.  
  118.             myErr = AudConv_GetOutputFormat(&myDestInfo);
  119.             if (myErr == noErr) {
  120.                 if (myOutputAIFF)
  121.                     myErr = AudConv_ConvertToAIFF(&myInputFSSpec, &myReply.sfFile, &myDestInfo);
  122.                 else
  123.                     myErr = AudConv_ConvertToMovie(&myInputFSSpec, &myReply.sfFile, &myDestInfo);
  124.                 FailIf(myErr != noErr, Failure);
  125.             }
  126.             
  127.             if (myErr == userCanceledErr)
  128.                 myErr = noErr;
  129.             FailIf(myErr != noErr, Failure);
  130.         }
  131.     }
  132.  
  133. Failure:
  134.     ExitMovies();
  135. Exit:
  136.  
  137. #if TARGET_OS_WIN32
  138.     // terminate the QuickTime Media Layer
  139.     TerminateQTML();
  140.     return(1);
  141. #endif    
  142.  
  143. #if TARGET_OS_MAC
  144.     return;
  145. #endif    
  146. }
  147.  
  148.  
  149. //////////
  150. //
  151. // AudConv_ConvertToAIFF 
  152. // Read the source AIFF file and convert to the destination format; output a new AIFF file.
  153. // The new file must include the compression parameters chunk, if used.
  154. //
  155. //////////
  156.  
  157. OSErr AudConv_ConvertToAIFF (ConstFSSpecPtr theInputFSSpec, ConstFSSpecPtr theOutputFSSpec, SoundComponentDataPtr theDestInfo)
  158. {
  159.     SoundComponentData        mySourceInfo;
  160.     Handle                    mySourceCompParamsHandle = NULL;
  161.     Handle                    myDestHandle = NULL;
  162.     Handle                    myDestCompParamsHandle = NULL;
  163.     long                    mySourceOffset;
  164.     long                    mySourceSize;
  165.     short                    myInputFile;
  166.     short                    myOutputFile;
  167.     long                    myCount;
  168.     OSErr                    myErr = noErr;
  169.  
  170.     // open the source file and parse it
  171.     myErr = FSpOpenDF(theInputFSSpec, fsRdPerm, &myInputFile);
  172.     FailIf(myErr != noErr, NoInputFile);
  173.  
  174.     myErr = AudConv_GetDataFromAIFF(myInputFile, &mySourceInfo, &mySourceOffset, &mySourceSize, &mySourceCompParamsHandle);
  175.     FailIf(myErr != noErr, NoData);
  176.  
  177.     // create the buffer into which we read the source data
  178.     mySourceInfo.buffer = (Byte *)NewPtrClear(mySourceSize);
  179.     FailWithAction(mySourceInfo.buffer == NULL, myErr = MemError(), NoSource);
  180.  
  181.     // create the buffer to hold the converted data
  182.     myDestHandle = NewHandle(0);
  183.     FailWithAction(myDestHandle == NULL, myErr = MemError(), NoDest);
  184.  
  185.     // read the source data from the source file into the source data buffer
  186.     myErr = SetFPos(myInputFile, fsFromStart, mySourceOffset);
  187.     FailIf(myErr != noErr, InputFileErr);
  188.  
  189.     myErr = FSRead(myInputFile, &mySourceSize, mySourceInfo.buffer);
  190.     FailIf(myErr != noErr, InputFileErr);
  191.  
  192.     // create the output AIFF file, to hold the converted data
  193.     myErr = FSpCreate(theOutputFSSpec, sigMoviePlayer, AIFFID, smSystemScript);
  194.     FailIf(myErr != noErr, CreateOutputErr);
  195.  
  196.     myErr = FSpOpenDF(theOutputFSSpec, fsRdWrPerm, &myOutputFile);
  197.     FailIf(myErr != noErr, OutputFileErr);
  198.  
  199.     // convert the source data into the output data
  200.     myErr = AudConv_ConvertAudioIntoHandle(&mySourceInfo, mySourceCompParamsHandle, myDestHandle, theDestInfo, &myDestCompParamsHandle, NULL);
  201.     FailIf(myErr != noErr, ConvertErr);
  202.  
  203.     // put the output data into the output AIFF file
  204.     myErr = AudConv_PrepFileAsAIFF(myOutputFile, theDestInfo, myDestCompParamsHandle);
  205.     FailIf(myErr != noErr, ConvertErr);
  206.  
  207.     HLock(myDestHandle);
  208.     myCount = GetHandleSize(myDestHandle);
  209.     myErr = FSWrite(myOutputFile, &myCount, *myDestHandle);
  210.     HUnlock(myDestHandle);
  211.     FailIf(myErr != noErr, ConvertErr);
  212.  
  213.     myErr = AudConv_FinishFileAsAIFF(myOutputFile, theDestInfo->sampleCount, myCount);
  214.     FailIf(myErr != noErr, ConvertErr);
  215.  
  216.     // close the input and output files
  217.     myErr = FSClose(myInputFile);
  218.     FailIf(myErr != noErr, ConvertErr);
  219.  
  220.     myErr = FSClose(myOutputFile);
  221.     FailIf(myErr != noErr, ConvertErr);
  222.  
  223.     // dispose of any storage we allocated and no longer need
  224.     if (mySourceInfo.buffer != NULL)
  225.         DisposePtr((Ptr)mySourceInfo.buffer);
  226.     
  227.     if (myDestHandle != NULL)
  228.         DisposeHandle(myDestHandle);
  229.         
  230.     if (mySourceCompParamsHandle != NULL)
  231.         DisposeHandle(mySourceCompParamsHandle);
  232.         
  233.     if (myDestCompParamsHandle != NULL)
  234.         DisposeHandle(myDestCompParamsHandle);
  235.     
  236.     return(noErr);
  237.  
  238. ConvertErr:
  239.     FSClose(myOutputFile);
  240.     
  241. OutputFileErr:
  242.     FSpDelete(theOutputFSSpec);
  243.     
  244. CreateOutputErr:
  245. InputFileErr:
  246.     if (myDestHandle != NULL)
  247.         DisposeHandle(myDestHandle);
  248.     
  249. NoDest:
  250.     if (mySourceInfo.buffer != NULL)
  251.         DisposePtr((Ptr)mySourceInfo.buffer);
  252.     
  253. NoSource:
  254.     if (mySourceCompParamsHandle != NULL)
  255.         DisposeHandle(mySourceCompParamsHandle);
  256.         
  257. NoData:
  258.     FSClose(myInputFile);
  259.     
  260. NoInputFile:
  261.     return(myErr);
  262. }
  263.  
  264.  
  265. //////////
  266. //
  267. // AudConv_ConvertToMovie
  268. // Read the source AIFF file and convert to the destination format; output a new movie file.
  269. // The new file must include the compression parameters chunk, if used.
  270. //
  271. //////////
  272.  
  273. OSErr AudConv_ConvertToMovie (ConstFSSpecPtr theInputFSSpec, ConstFSSpecPtr theOutputFSSpec, SoundComponentDataPtr theDestInfo)
  274. {
  275.     SoundComponentData        mySourceInfo;
  276.     Handle                    mySourceCompParamsHandle = NULL;
  277.     Handle                    myDestHandle = NULL;
  278.     Handle                    myDestCompParamsHandle = NULL;
  279.     long                    mySourceOffset;
  280.     long                    mySourceSize;
  281.     short                    myInputFile;
  282.     CompressionInfo            myDestCompInfo;
  283.     Movie                    myMovie;
  284.     Track                    myTrack;
  285.     short                    myResRefNum = -1;
  286.     short                    myResID = movieInDataForkResID;
  287.     long                    myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile;
  288.     OSErr                    myErr = noErr;
  289.  
  290.     // open the source file and parse it
  291.     myErr = FSpOpenDF(theInputFSSpec, fsRdPerm, &myInputFile);
  292.     FailIf(myErr != noErr, NoInputFile);
  293.  
  294.     myErr = AudConv_GetDataFromAIFF(myInputFile, &mySourceInfo, &mySourceOffset, &mySourceSize, &mySourceCompParamsHandle);
  295.     FailIf(myErr != noErr, NoData);
  296.  
  297.     // create the buffer into which we read the source data
  298.     mySourceInfo.buffer = (Byte *)NewPtrClear(mySourceSize);
  299.     FailWithAction(mySourceInfo.buffer == NULL, myErr = MemError(), NoSource);
  300.  
  301.     // create the buffer to hold the converted data
  302.     myDestHandle = NewHandle(0);
  303.     FailWithAction(myDestHandle == NULL, myErr = MemError(), NoDest);
  304.  
  305.     // read the source data from the source file into the source data buffer
  306.     myErr = SetFPos(myInputFile, fsFromStart, mySourceOffset);
  307.     FailIf(myErr != noErr, InputFileErr);
  308.  
  309.     myErr = FSRead(myInputFile, &mySourceSize, mySourceInfo.buffer);
  310.     FailIf(myErr != noErr, InputFileErr);
  311.  
  312.     // create the output movie file, to hold the converted data
  313.     myErr = CreateMovieFile(theOutputFSSpec, sigMoviePlayer, smCurrentScript, myFlags, &myResRefNum, &myMovie);
  314.     FailIf(myErr != noErr, CreateOutputErr);
  315.  
  316.     // convert the source data into the output data
  317.     myErr = AudConv_ConvertAudioIntoHandle(&mySourceInfo, mySourceCompParamsHandle, myDestHandle, theDestInfo, &myDestCompParamsHandle, &myDestCompInfo);
  318.     FailIf(myErr != noErr, CompressErr);
  319.  
  320.     // put the output data into a track in the output movie file
  321.     myTrack = NewMovieTrack(myMovie, 0, 0, kFullVolume);
  322.     myErr = GetMoviesError();
  323.     FailIf(myErr != noErr, CompressErr);
  324.  
  325.     myErr = AudConv_PutAudioIntoTrack(myTrack, myDestHandle, theDestInfo, myDestCompParamsHandle, &myDestCompInfo);
  326.     FailIf(myErr != noErr, CompressErr);
  327.  
  328.     myErr = AddMovieResource(myMovie, myResRefNum, &myResID, NULL);
  329.     FailIf(myErr != noErr, CompressErr);
  330.  
  331.     // close the movie and movie file
  332.     if (myResRefNum != -1)
  333.         CloseMovieFile(myResRefNum);
  334.     
  335.     if (myMovie != NULL)
  336.         DisposeMovie(myMovie);
  337.  
  338.     // dispose of any storage we allocated and no longer need
  339.     if (mySourceInfo.buffer != NULL)
  340.         DisposePtr((Ptr)mySourceInfo.buffer);
  341.     
  342.     if (myDestHandle != NULL)
  343.         DisposeHandle(myDestHandle);
  344.         
  345.     if (mySourceCompParamsHandle != NULL)
  346.         DisposeHandle(mySourceCompParamsHandle);
  347.         
  348.     if (myDestCompParamsHandle != NULL)
  349.         DisposeHandle(myDestCompParamsHandle);
  350.         
  351.     return(noErr);
  352.  
  353. CompressErr:
  354.     if (myMovie != NULL)
  355.         DisposeMovie(myMovie);
  356.         
  357.     if (myResRefNum != -1)
  358.         CloseMovieFile(myResRefNum);
  359.         
  360. CreateOutputErr:
  361. InputFileErr:
  362.     if (mySourceCompParamsHandle != NULL)
  363.         DisposeHandle(mySourceCompParamsHandle);
  364.         
  365. NoDest:
  366.     if (mySourceInfo.buffer != NULL)
  367.         DisposePtr((Ptr)mySourceInfo.buffer);
  368.     
  369. NoSource:
  370.     if (mySourceCompParamsHandle != NULL)
  371.         DisposeHandle(mySourceCompParamsHandle);
  372.         
  373. NoData:
  374.     FSClose(myInputFile);
  375.     
  376. NoInputFile:
  377.     return(myErr);
  378. }
  379.  
  380.  
  381. //////////
  382. //
  383. // AudConv_GetDataFromAIFF
  384. // Parse the specified file and read the data in the required chunks in it.
  385. //
  386. // This is a very simple AIFF parser which will read three basic chunks: the
  387. // main ContainerChunk, the Common or ExtCommonChunk, and the SoundDataChunk.
  388. // That's all that's required. One last chunk is read if found, which contains the
  389. // decompression parameters required to decompress this audio. It's a simple
  390. // chunk with unknown data contained within. Copy this data into a handle, and
  391. // it becomes the audio atoms used with the siDecompressionParams selector.
  392. //
  393. // Keep in mind that the data in an AIFF file is always big-endian.
  394. //
  395. //////////
  396.  
  397. OSErr AudConv_GetDataFromAIFF (short theRefNum, SoundComponentDataPtr theSourceInfo, long *theSourceOffset, long *theSourceSize, Handle *theDestCompParams)
  398. {
  399.     union {
  400.         ContainerChunk    cnt;
  401.         CommonChunk        com;
  402.         ExtCommonChunk    ext;
  403.         SoundDataChunk    snd;
  404.     }                     myChunk;
  405.     ChunkHeader            myCHeader;
  406.     Handle                myDestCompParamsHandle;
  407.     long double            mySampleRate;
  408.     long                myOffset;
  409.     long                myDataSize;
  410.     long                myByteCount;
  411.     OSErr                myErr = noErr;
  412.  
  413.     myErr = SetFPos(theRefNum, fsFromStart, 0);        // we have to start at the beginning
  414.     FailIf(myErr != noErr, Failure);
  415.  
  416.     // everything is contained in a container chunk, which therefore is the first chunk in the file;
  417.     // read the beginning of the container chunk into memory
  418.     myByteCount = sizeof(ContainerChunk);
  419.     myErr = FSRead(theRefNum, &myByteCount, &myChunk);
  420.     FailIf(myErr != noErr, Failure);
  421.  
  422.     // make sure it's a FORM chunk
  423.     FailWithAction(EndianU32_BtoN(myChunk.cnt.ckID) != FORMID, myErr = badFileFormat, Failure);
  424.  
  425.     // find the CommonChunk or ExtCommonChunk
  426.     myErr = AudConv_SetFPosToChunk(theRefNum, CommonID);
  427.     FailIf(myErr != noErr, Failure);
  428.  
  429.     switch (EndianU32_BtoN(myChunk.cnt.formType)) {
  430.         case AIFFID:
  431.             myByteCount = sizeof(CommonChunk);
  432.             myErr = FSRead(theRefNum, &myByteCount, &myChunk);
  433.             FailIf(myErr != noErr, Failure);
  434.  
  435.             theSourceInfo->format = k16BitBigEndianFormat;
  436.             break;
  437.  
  438.         case AIFCID:
  439.             myByteCount = offsetof(ExtCommonChunk, compressionName);
  440.             myErr = FSRead(theRefNum, &myByteCount, &myChunk);
  441.             FailIf(myErr != noErr, Failure);
  442.  
  443.             theSourceInfo->format = EndianU32_BtoN(myChunk.ext.compressionType);
  444.             break;
  445.  
  446.         default:
  447.             FailWithAction(badFileFormat, myErr = badFileFormat, Failure);
  448.             break;
  449.     }
  450.     
  451.     theSourceInfo->flags = 0L;
  452.     theSourceInfo->numChannels = EndianS16_BtoN(myChunk.com.numChannels);
  453.     theSourceInfo->sampleSize = EndianS16_BtoN(myChunk.com.sampleSize);
  454. #if TARGET_CPU_68K
  455.     mySampleRate = myChunk.com.sampleRate;
  456. #else
  457.     x80told(&myChunk.com.sampleRate, &mySampleRate);
  458. #endif
  459.     theSourceInfo->sampleRate = (unsigned long)(mySampleRate * 65536.0);
  460.     theSourceInfo->sampleCount = EndianU32_BtoN(myChunk.com.numSampleFrames);
  461.     theSourceInfo->buffer = NULL;
  462.     theSourceInfo->reserved = 0;
  463.  
  464.     myErr = AudConv_SetFPosToChunk(theRefNum, SoundDataID);
  465.     FailIf(myErr != noErr, Failure);
  466.  
  467.     myByteCount = sizeof(SoundDataChunk);
  468.     myErr = FSRead(theRefNum, &myByteCount, &myChunk);
  469.     FailIf(myErr != noErr, Failure);
  470.  
  471.     myDataSize = EndianS32_BtoN(myChunk.snd.ckSize) - sizeof(SoundDataChunk);    // size of source data
  472.     myErr = GetFPos(theRefNum, &myOffset);
  473.     FailIf(myErr != noErr, Failure);
  474.  
  475.     // return NULL if no siDecompressionParams
  476.     myDestCompParamsHandle = NULL;
  477.     myErr = AudConv_SetFPosToChunk(theRefNum, siDecompressionParams);
  478.     if (myErr == noErr) {
  479.         myByteCount = sizeof(myCHeader);
  480.         myErr = FSRead(theRefNum, &myByteCount, &myCHeader);
  481.         FailIf(myErr != noErr, Failure);
  482.  
  483.         myByteCount = EndianS32_BtoN(myCHeader.ckSize);
  484.         myDestCompParamsHandle = NewHandle(myByteCount);
  485.         FailIf(myDestCompParamsHandle == NULL, Failure);
  486.  
  487.         HLock(myDestCompParamsHandle);
  488.         myErr = FSRead(theRefNum, &myByteCount, *myDestCompParamsHandle);
  489.         HUnlock(myDestCompParamsHandle);
  490.         FailIf(myErr != noErr, ReadParamsErr);
  491.     }
  492.     
  493.     *theSourceOffset = myOffset;                        // offset from start of file to audio samples
  494.     *theSourceSize = myDataSize;                        // size in bytes of the audio samples
  495.     *theDestCompParams = myDestCompParamsHandle;        // possible handle to the decompression parameters
  496.     return(noErr);
  497.  
  498. ReadParamsErr:
  499.     if (myDestCompParamsHandle != NULL)
  500.         DisposeHandle(myDestCompParamsHandle);
  501.         
  502. Failure:
  503.     return(myErr);
  504. }
  505.  
  506.  
  507. //////////
  508. //
  509. // AudConv_ConvertAudioIntoHandle
  510. // Convert the specified sound data.
  511. //
  512. //////////
  513.  
  514. OSErr AudConv_ConvertAudioIntoHandle (    SoundComponentDataPtr theSourceInfo, 
  515.                                         Handle theSourceCompParamsHandle,
  516.                                         Handle theDestHandle, 
  517.                                         SoundComponentDataPtr theDestInfo,
  518.                                         Handle *theDestCompParamsHandle, 
  519.                                         CompressionInfoPtr theDestCompInfo)
  520. {
  521.     SoundConverter        myConverter;
  522.     Handle                myDestCompParamsHandle = NULL;            
  523.     CompressionInfo        myCompressionInfo;
  524.     unsigned short        mySourceBytesPerFrame;
  525.     UInt16                hasOptionsDialog = 0;
  526.     OSErr                myErr = noErr;
  527.  
  528.     // perform in non-real time for best quality
  529.     theDestInfo->flags |= kNoRealtimeProcessing;            
  530.  
  531.     //////////
  532.     //                
  533.     // open a sound converter
  534.     //                
  535.     //////////
  536.  
  537.     myErr = SoundConverterOpen(theSourceInfo, theDestInfo, &myConverter);
  538.     FailIf(myErr != noErr, Exit);
  539.  
  540.     //////////
  541.     //                
  542.     // display the options dialog for the sound converter
  543.     //
  544.     // the GetInfo call isn't required; you can just call SetInfo and it will show the dialog
  545.     // or return the same error that GetInfo would have; you might call GetInfo to determine
  546.     // whether the options dialog is supported, perhaps because you want to show a button that
  547.     // would bring up this dialog
  548.     //                
  549.     //////////
  550.  
  551.     myErr = SoundConverterGetInfo(myConverter, siOptionsDialog, &hasOptionsDialog);
  552.     if ((myErr == noErr) && (hasOptionsDialog != 0)) {
  553.         myErr = SoundConverterSetInfo(myConverter, siOptionsDialog, NULL);
  554.         FailIf(myErr != noErr, DialogErr);
  555.     }
  556.  
  557.     // set the number of output channels
  558.     myErr = SoundConverterSetInfo(myConverter, siCompressionChannels, &theDestInfo->numChannels);
  559.     // ignore this error, some codecs don't use this selector (makes QDesign work)
  560.  
  561.     //////////
  562.     //                
  563.     // get any custom decompression settings
  564.     //
  565.     // not all sound compressors have custom settings, in which case myDestCompParamsHandle
  566.     // will be returned unchanged (NULL); we return these custom settings to the caller, who
  567.     // will put them in the proper place; if this audio is stored in a QuickTime movie, these
  568.     // settings will be stored in a SoundDescriptionExtension of type siDecompressionParams
  569.     //
  570.     //////////
  571.  
  572.     SoundConverterGetInfo(myConverter, siCompressionParams, &myDestCompParamsHandle);    
  573.     
  574.     // send the source and destination compression settings to the sound converter; this is
  575.     // necessary to make some sound converters (for instance, QDesign) work properly
  576.     if (myDestCompParamsHandle != NULL) {
  577.         HLockHi(myDestCompParamsHandle);
  578.         myErr = SoundConverterSetInfo(myConverter, siCompressionParams, *myDestCompParamsHandle);
  579.         HUnlock(myDestCompParamsHandle);
  580.         FailIf(myErr != noErr, ConverterErr);
  581.     }
  582.  
  583.     if (theSourceCompParamsHandle != NULL) {
  584.         HLockHi(theSourceCompParamsHandle);
  585.         myErr = SoundConverterSetInfo(myConverter, siDecompressionParams, *theSourceCompParamsHandle);
  586.         HUnlock(theSourceCompParamsHandle);
  587.         FailIf(myErr != noErr, ConverterErr);
  588.     }
  589.  
  590.     *theDestCompParamsHandle = myDestCompParamsHandle;
  591.  
  592.     //////////
  593.     //                
  594.     // get the sizes of the source and destination sample frames
  595.     //
  596.     // we return the destination sample frame size to the caller, who will put it in the
  597.     // proper place; if this audio data is stored in a QuickTime movie, this size will be
  598.     // stored the Version 1 sound description handle
  599.     //                
  600.     //////////
  601.  
  602.     // get the source number of bytes in each sample frame
  603.     myErr = SoundConverterGetInfo(myConverter, siCompressionFactor, &myCompressionInfo);
  604.     if ((myErr != noErr) || (myCompressionInfo.format != theSourceInfo->format)) {
  605.         myErr = GetCompressionInfo(fixedCompression, theSourceInfo->format, theSourceInfo->numChannels, theSourceInfo->sampleSize, &myCompressionInfo);
  606.         FailIf(myErr != noErr, ConverterErr);
  607.     }
  608.     
  609.     // bytesPerFrame is not filled in by GetCompressionInfo, so we set it here
  610.     mySourceBytesPerFrame = myCompressionInfo.bytesPerPacket * theSourceInfo->numChannels;
  611.     
  612.     // get the destination number of bytes in each sample frame
  613.     if (theDestCompInfo != NULL) {
  614.         // get info about destination compression.
  615.         myErr = SoundConverterGetInfo(myConverter, siCompressionFactor, theDestCompInfo);
  616.         if ((myErr != noErr) || (theDestCompInfo->format != theDestInfo->format)) {
  617.             myErr = GetCompressionInfo(fixedCompression, theDestInfo->format, theDestInfo->numChannels, theDestInfo->sampleSize, theDestCompInfo);
  618.             FailIf(myErr != noErr, ConverterErr);
  619.         }
  620.         
  621.         // bytesPerFrame is not filled in by GetCompressionInfo, so we set it here
  622.         theDestCompInfo->bytesPerFrame = theDestCompInfo->bytesPerPacket * theDestInfo->numChannels;
  623.     }
  624.     
  625.     //////////
  626.     //                
  627.     // do the actual data conversion
  628.     //
  629.     //////////
  630.  
  631.     myErr = AudConv_WriteAudioToHandle(myConverter, theSourceInfo, mySourceBytesPerFrame, theDestInfo, theDestHandle);
  632.     FailIf(myErr != noErr, ConverterErr);
  633.  
  634.     SoundConverterClose(myConverter);
  635.     return(noErr);
  636.  
  637. ConverterErr:
  638.     if (myDestCompParamsHandle != NULL)
  639.         DisposeHandle(myDestCompParamsHandle);
  640.         
  641. DialogErr:
  642.     if (myConverter != NULL)
  643.         SoundConverterClose(myConverter);
  644.     
  645. Exit:
  646.     return(myErr);
  647. }
  648.  
  649.  
  650. //////////
  651. //
  652. // AudConv_WriteAudioToHandle
  653. // Convert the source data into an ever-expanding handle of the given format.
  654. // Of course this means you've only got one shot at converting the data, and it
  655. // all has to fit into memory. Converting larger amounts is left to the reader.
  656. //
  657. //////////
  658.  
  659. OSErr AudConv_WriteAudioToHandle (SoundConverter theConverter, SoundComponentDataPtr theSourceInfo, short theSourceBytesPerFrame, SoundComponentDataPtr theDestInfo, Handle theDestHandle)
  660. {
  661.     unsigned long    myNumFramesLeft;
  662.     unsigned long    mySourceFrames;
  663.     unsigned long    myDestFrames;
  664.     unsigned long    mySourceBytes;
  665.     unsigned long    myDestBytes;
  666.     unsigned long    myDataOffset = 0;
  667.     Ptr                myDestPtr = NULL;
  668.     OSErr            myErr = noErr;
  669.  
  670.     // get sound converter buffer size info
  671.     myErr = SoundConverterGetBufferSizes(theConverter, kMaxSndConvtBufferSize, &mySourceFrames, &mySourceBytes, &myDestBytes);
  672.     FailIf(myErr != noErr, Exit);
  673.  
  674.     // create destination data buffer
  675.     myDestPtr = NewPtrClear(myDestBytes);
  676.     FailWithAction(myErr != noErr, myErr = MemError(), Exit);
  677.  
  678.     //////////
  679.     //
  680.     // convert the sound to the desired output format
  681.     //
  682.     //////////
  683.  
  684.     myErr = SoundConverterBeginConversion(theConverter);
  685.     FailIf(myErr != noErr, ConverterErr);
  686.  
  687.     // initialize destination total frame count to zero
  688.     theDestInfo->sampleCount = 0;
  689.     myNumFramesLeft = (unsigned long)theSourceInfo->sampleCount;
  690.     
  691.     while (myNumFramesLeft > 0) {
  692.         // if there are fewer frames remaining than our source buffer size,
  693.         // we're near the end of our data, so get just what's remaining
  694.         if (mySourceFrames > myNumFramesLeft)
  695.             mySourceFrames = myNumFramesLeft;
  696.  
  697.         myErr = SoundConverterConvertBuffer(theConverter, theSourceInfo->buffer + myDataOffset, mySourceFrames, myDestPtr, &myDestFrames, &myDestBytes);
  698.         FailIf(myErr != noErr, ConverterErr);
  699.  
  700.         // write this data to the output handle
  701.         if (myDestBytes > 0) {
  702.             myErr = PtrAndHand(myDestPtr, theDestHandle, myDestBytes);
  703.             FailIf(myErr != noErr, ConverterErr);
  704.         }
  705.  
  706.         // move offset to appropriate place in source data
  707.         myDataOffset += mySourceFrames * theSourceBytesPerFrame;
  708.         
  709.         // keep track of total frames returned by converter
  710.         theDestInfo->sampleCount += myDestFrames;
  711.         
  712.         myNumFramesLeft -= mySourceFrames;
  713.     }
  714.  
  715.     // end the conversion, and see if we get back a few more bytes of data    
  716.     myErr = SoundConverterEndConversion(theConverter, myDestPtr, &myDestFrames, &myDestBytes);
  717.     FailIf(myErr != noErr, ConverterErr);
  718.  
  719.     if (myDestBytes > 0) {
  720.         // write this data to the output handle
  721.         myErr = PtrAndHand(myDestPtr, theDestHandle, myDestBytes);
  722.         FailIf(myErr != noErr, ConverterErr);
  723.     }
  724.     
  725.     theDestInfo->sampleCount += myDestFrames;            // update final amount of samples
  726.  
  727. ConverterErr:
  728.     if (myDestPtr != NULL)
  729.         DisposePtr(myDestPtr);
  730.         
  731. Exit:
  732.     return(myErr);
  733. }
  734.  
  735.  
  736. //////////
  737. //
  738. // AudConv_GetOutputFormat
  739. // Display the QuickTime standard sound settings dialog to obtain the output format.
  740. //
  741. //////////
  742.  
  743. OSErr AudConv_GetOutputFormat (SoundComponentDataPtr theDestInfo)
  744. {
  745.     ComponentInstance    myComponent = NULL;
  746.     ComponentResult        myErr = noErr;
  747.  
  748.     myComponent = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubTypeSound);
  749.     FailWithAction(myComponent == NULL, myErr = cantFindHandler, NoComponent);
  750.  
  751.     myErr = SCRequestImageSettings(myComponent);            // show the dialog to user
  752.     if (myErr == noErr) {                                    // then get the settings
  753.         myErr = SCGetInfo(myComponent, scSoundCompressionType, &theDestInfo->format);
  754.         FailIf(myErr != noErr, SCGetInfoFailed);
  755.  
  756.         myErr = SCGetInfo(myComponent, scSoundChannelCountType, &theDestInfo->numChannels);
  757.         FailIf(myErr != noErr, SCGetInfoFailed);
  758.  
  759.         myErr = SCGetInfo(myComponent, scSoundSampleSizeType, &theDestInfo->sampleSize);
  760.         FailIf(myErr != noErr, SCGetInfoFailed);
  761.  
  762.         myErr = SCGetInfo(myComponent, scSoundSampleRateType, &theDestInfo->sampleRate);
  763.         FailIf(myErr != noErr, SCGetInfoFailed);
  764.  
  765.         // any non-compressed format appears as 'raw ', which is not what the Sound Manager expects
  766.         if (theDestInfo->format == k8BitOffsetBinaryFormat) {
  767.             if (theDestInfo->sampleSize > 8)
  768.                 theDestInfo->format = k16BitBigEndianFormat;
  769.             else
  770.                 theDestInfo->format = kSoundNotCompressed;
  771.         }
  772.  
  773.         // clear the rest of the unused parameters
  774.         theDestInfo->flags = 0;
  775.         theDestInfo->sampleCount = 0;
  776.         theDestInfo->buffer = NULL;
  777.         theDestInfo->reserved = 0L;
  778.     }
  779.     
  780.     return((OSErr)myErr);
  781.  
  782. SCGetInfoFailed:
  783.     CloseComponent(myComponent);
  784.     
  785. NoComponent:
  786.     return((OSErr)myErr);
  787. }
  788.  
  789.  
  790. //////////
  791. //
  792. // AudConv_SetFPosToChunk
  793. // Set the file's position to the start of the given chunk.
  794. //
  795. //////////
  796.  
  797. OSErr AudConv_SetFPosToChunk (short theRefNum, ID theChunkID)
  798. {
  799.     long                myCount;
  800.     ChunkHeader            myCHeader;
  801.     OSErr                myErr = noErr;
  802.  
  803.     // position the file mark past the ContainerChunk, which is always the first thing in the file
  804.     myErr = SetFPos(theRefNum, fsFromStart, sizeof(ContainerChunk));
  805.     FailIf(myErr != noErr, Exit);
  806.  
  807.     while (true) {
  808.         myCount = sizeof(myCHeader);
  809.         myErr = FSRead(theRefNum, &myCount, &(myCHeader.ckID));
  810.         if (myErr == eofErr)                            // reached the end of file, exit gracefully
  811.             break;
  812.         FailIf(myErr != noErr, Exit);
  813.  
  814.         if (theChunkID == EndianU32_BtoN(myCHeader.ckID)) {
  815.             myCount = -myCount;                            // rewind ptr to top of chunk if found
  816.             myErr = SetFPos(theRefNum, fsFromMark, myCount);
  817.             FailIf(myErr != noErr, Exit);
  818.             break;
  819.         } else {                                        // else skip over the entire chunk
  820.             myErr = SetFPos(theRefNum, fsFromMark, EndianS32_BtoN(myCHeader.ckSize));
  821.             if (myErr == eofErr)                        // reached the end of file, exit gracefully
  822.                 break;
  823.             FailIf(myErr != noErr, Exit);
  824.         }
  825.     }
  826.  
  827. Exit:
  828.     return(myErr);
  829. }
  830.  
  831.  
  832. //////////
  833. //
  834. // AudConv_PrepFileAsAIFF
  835. // Write out the necessary chunks, without audio samples, to prepare for writing;
  836. // leave file position at the proper place for audio samples.
  837. //
  838. //////////
  839.  
  840. OSErr AudConv_PrepFileAsAIFF (short theOutputFile, SoundComponentDataPtr theDestInfo, Handle theDestParams)
  841. {
  842.     Str255                myCompressionName;
  843.     ContainerChunk        myContainer;
  844.     FormatVersionChunk    myVersion;
  845.     ExtCommonChunk        myCommon;
  846.     SoundDataChunk        mySoundData;
  847.     ID                    myFormType;
  848.     long                myCount;
  849.     OSErr                myErr = noErr;
  850.  
  851.     switch (theDestInfo->format) {
  852.         case kSoundNotCompressed:
  853.         case k8BitOffsetBinaryFormat:
  854.         case k16BitBigEndianFormat:
  855.         case k24BitFormat:
  856.         case k32BitFormat:
  857.             myFormType = AIFFID;
  858.             break;
  859.  
  860.         default :
  861.             myFormType = AIFCID;
  862.             break;
  863.     }
  864.  
  865.     myErr = SetFPos(theOutputFile, fsFromStart, 0);
  866.     FailIf(myErr != noErr, Exit);
  867.  
  868.     // write out the first chunk
  869.     myContainer.ckID = EndianU32_NtoB(FORMID);
  870.     myContainer.ckSize = EndianU32_NtoB(0);                                    // nothing in the file just yet
  871.     myContainer.formType = EndianU32_NtoB(myFormType);
  872.     myCount = sizeof(myContainer);
  873.     myErr = FSWrite(theOutputFile, &myCount, &myContainer);
  874.     FailIf(myErr != noErr, Exit);
  875.  
  876.     // write out the version chunk for AIFC files
  877.     if (myFormType == AIFCID) {
  878.         myVersion.ckID = EndianU32_NtoB(FormatVersionID);
  879.         myVersion.ckSize = EndianS32_NtoB(sizeof(FormatVersionChunk) - sizeof(ChunkHeader));
  880.         myVersion.timestamp = EndianU32_NtoB(AIFCVersion1);                    // timestamp for this version
  881.         myCount = sizeof(myVersion);
  882.         myErr = FSWrite(theOutputFile, &myCount, &myVersion);
  883.         FailIf(myErr != noErr, Exit);
  884.     }
  885.  
  886.     // write out the CommonChunk or ExtCommonChunk if the data is compressed
  887.     myCommon.ckID = EndianU32_NtoB(CommonID);
  888.     myCommon.ckSize = EndianS32_NtoB(sizeof(CommonChunk) - sizeof(ChunkHeader));    // size of common chunk variables without compression info
  889.     myCommon.numChannels = EndianS16_NtoB(theDestInfo->numChannels);
  890.     myCommon.numSampleFrames = EndianU32_NtoB(0);                            // nothing in the file just yet
  891.     myCommon.sampleSize = EndianS16_NtoB(theDestInfo->sampleSize);
  892. #if TARGET_CPU_68K
  893.     myCommon.sampleRate = theDestInfo->sampleRate / 65536.0;
  894. #else
  895.     {
  896.         long double        ldRate;
  897.  
  898.         ldRate = theDestInfo->sampleRate / 65536.0;
  899.         ldtox80(&ldRate, &myCommon.sampleRate);
  900.     }
  901. #endif
  902.     myCommon.compressionType = EndianU32_NtoB(theDestInfo->format);
  903.     myCommon.compressionName[0] = 0;
  904.  
  905.     if (myFormType == AIFCID) {
  906.         myErr = GetCompressionName(theDestInfo->format, myCompressionName);
  907.         if (myErr == noErr) {
  908.             // write out the ExtCommonChunk for compressed data, with the name
  909.             myCount = offsetof(ExtCommonChunk, compressionName);
  910.             myCommon.ckSize = myCount - sizeof(ChunkHeader) + myCompressionName[0] + 1;
  911.             myCommon.ckSize = ++myCommon.ckSize & ~1;            // must be word aligned
  912.             myCommon.ckSize = EndianS32_NtoB(myCommon.ckSize);
  913.             myErr = FSWrite(theOutputFile, &myCount, &myCommon);
  914.             FailIf(myErr != noErr, Exit);
  915.  
  916.             myCount = myCompressionName[0] + 1;
  917.             myCount = ++myCount & ~1;                            // must be word aligned
  918.             myErr = FSWrite(theOutputFile, &myCount, &myCompressionName);
  919.             FailIf(myErr != noErr, Exit);
  920.         } else {
  921.             // write out the ExtCommonChunk for compressed data, without the name
  922.             myCommon.ckSize = EndianS32_NtoB(sizeof(ExtCommonChunk) - sizeof(ChunkHeader));    // size of common chunk variables without compression info
  923.             myCount = offsetof(ExtCommonChunk, compressionName);
  924.             myErr = FSWrite(theOutputFile, &myCount, &myCommon);
  925.             FailIf(myErr != noErr, Exit);
  926.         }
  927.     } else {    // write out the CommonChunk for non-compressed data
  928.         myCount = sizeof(CommonChunk);
  929.         myErr = FSWrite(theOutputFile, &myCount, &myCommon);
  930.         FailIf(myErr != noErr, Exit);
  931.     }
  932.  
  933.     if (theDestParams != NULL) {
  934.         ChunkHeader        myCHeader;
  935.  
  936.         myCHeader.ckID = EndianU32_NtoB(siDecompressionParams);
  937.         myCHeader.ckSize = EndianS32_NtoB(GetHandleSize(theDestParams));
  938.         myCount = sizeof(myCHeader);
  939.         myErr = FSWrite(theOutputFile, &myCount, &myCHeader);
  940.         FailIf(myErr != noErr, Exit);
  941.  
  942.         myCount = EndianS32_BtoN(myCHeader.ckSize);
  943.         HLock(theDestParams);
  944.         myErr = FSWrite(theOutputFile, &myCount, *theDestParams);
  945.         FailIf(myErr != noErr, Exit);
  946.     }
  947.  
  948.     mySoundData.ckID = EndianU32_NtoB(SoundDataID);
  949.     mySoundData.ckSize = EndianS32_NtoB(0);                            // nothing in the file just yet
  950.     mySoundData.offset = EndianU32_NtoB(0);
  951.     mySoundData.blockSize = EndianU32_NtoB(0);
  952.     myCount = sizeof(mySoundData);
  953.     myErr = FSWrite(theOutputFile, &myCount, &mySoundData);
  954.     FailIf(myErr != noErr, Exit);
  955.  
  956.     // now go back and update Container to current file size
  957.     myErr = SetFPos(theOutputFile, fsFromStart, 0);
  958.     FailIf(myErr != noErr, Exit);
  959.  
  960.     myCount = sizeof(myContainer);
  961.     myErr = FSRead(theOutputFile, &myCount, &myContainer);
  962.     FailIf(myErr != noErr, Exit);
  963.  
  964.     myErr = GetEOF(theOutputFile, &myContainer.ckSize);
  965.     FailIf(myErr != noErr, Exit);
  966.  
  967.     myErr = SetFPos(theOutputFile, fsFromStart, 0);
  968.     FailIf(myErr != noErr, Exit);
  969.  
  970.     myContainer.ckSize -= sizeof(ChunkHeader);
  971.     myContainer.ckSize = EndianS32_NtoB(myContainer.ckSize);
  972.     myCount = sizeof(myContainer);
  973.     myErr = FSWrite(theOutputFile, &myCount, &myContainer);
  974.     FailIf(myErr != noErr, Exit);
  975.  
  976.     // set current file position to the end of end of the file
  977.     // the last chunk is the SoundDataChunk, and that's where to begin writing data
  978.     myErr = SetFPos(theOutputFile, fsFromStart, EndianS32_BtoN(myContainer.ckSize) + sizeof(ChunkHeader));
  979.     FailIf(myErr != noErr, Exit);
  980.  
  981. Exit:
  982.     return(myErr);
  983. }
  984.  
  985.  
  986. //////////
  987. //
  988. // AudConv_FinishFileAsAIFF
  989. // This assumes that all the necessary chunks have been written to the file and
  990. // then audio samples have been added. Finally, update the chunks to reflect the
  991. // samples that were added.
  992. //
  993. //////////
  994.  
  995. OSErr AudConv_FinishFileAsAIFF (short theOutputFile, unsigned long destFramesMoved, unsigned long destBytesMoved)
  996. {
  997.     CommonChunk            myCommon;
  998.     SoundDataChunk        mySoundData;
  999.     long                myCount;
  1000.     OSErr                myErr = noErr;
  1001.  
  1002.     // find the CommonChunk and update the numSampleFrames
  1003.     myErr = AudConv_SetFPosToChunk(theOutputFile, CommonID);
  1004.     FailIf(myErr != noErr, Exit);
  1005.  
  1006.     myCount = sizeof(myCommon);
  1007.     myErr = FSRead(theOutputFile, &myCount, &myCommon);
  1008.     FailIf(myErr != noErr, Exit);
  1009.  
  1010.     myErr = SetFPos(theOutputFile, fsFromMark, -myCount);        // reset to start of chunk
  1011.     FailIf(myErr != noErr, Exit);
  1012.  
  1013.     myCommon.numSampleFrames = EndianU32_NtoB(destFramesMoved);
  1014.     myCount = sizeof(myCommon);
  1015.     myErr = FSWrite(theOutputFile, &myCount, &myCommon);
  1016.     FailIf(myErr != noErr, Exit);
  1017.  
  1018.     // find the SoundDataChunk and update the ckSize
  1019.     myErr = AudConv_SetFPosToChunk(theOutputFile, SoundDataID);
  1020.     FailIf(myErr != noErr, Exit);
  1021.  
  1022.     myCount = sizeof(mySoundData);
  1023.     myErr = FSRead(theOutputFile, &myCount, &mySoundData);
  1024.     FailIf(myErr != noErr, Exit);
  1025.  
  1026.     myErr = SetFPos(theOutputFile, fsFromMark, -myCount);        // reset to start of chunk
  1027.     FailIf(myErr != noErr, Exit);
  1028.  
  1029.     mySoundData.ckSize = EndianS32_NtoB(sizeof(SoundDataChunk) - sizeof(ChunkHeader) + destBytesMoved);
  1030.     myCount = sizeof(mySoundData);
  1031.     myErr = FSWrite(theOutputFile, &myCount, &mySoundData);
  1032.     FailIf(myErr != noErr, Exit);
  1033.  
  1034. Exit:
  1035.     return(myErr);
  1036. }
  1037.  
  1038.  
  1039. //////////
  1040. //
  1041. // AudConv_PutAudioIntoTrack
  1042. // Add the audio as a track with the necessary decompression extension.
  1043. //
  1044. //////////
  1045.  
  1046. OSErr AudConv_PutAudioIntoTrack (    Track theTrack, 
  1047.                                     Handle theDestAudioData, 
  1048.                                     SoundComponentDataPtr theDestInfo,
  1049.                                     Handle theDestCompParamsHandle, 
  1050.                                     CompressionInfoPtr theDestCompInfo)
  1051. {
  1052.     SoundDescriptionV1Handle    mySampleDesc;
  1053.     Media                        myMedia;
  1054.     OSErr                        myErr = noErr;
  1055.  
  1056.     //////////
  1057.     //                
  1058.     // create a media for the track passed in
  1059.     //                
  1060.     //////////
  1061.  
  1062.     // set new track to be a sound track
  1063.     myMedia = NewTrackMedia(theTrack, SoundMediaType, theDestInfo->sampleRate >> 16, NULL, 0);
  1064.     myErr = GetMoviesError();
  1065.     FailIf(myErr != noErr, Exit);
  1066.  
  1067.     // start a media editing session
  1068.     myErr = BeginMediaEdits(myMedia);
  1069.     FailIf(myErr != noErr, Exit);
  1070.  
  1071.     //////////
  1072.     //                
  1073.     // create a sound sample description
  1074.     //                
  1075.     //////////
  1076.  
  1077.     // use the SoundDescription format 1 because it adds fields for data size information
  1078.     // and is required by AddSoundDescriptionExtension if an extension is required for the compression format
  1079.     mySampleDesc = (SoundDescriptionV1Handle)NewHandleClear(sizeof(SoundDescriptionV1));
  1080.     FailWithAction(myErr != noErr, myErr = MemError(), Exit);
  1081.  
  1082.     (**mySampleDesc).desc.descSize            = sizeof(SoundDescriptionV1);
  1083.     (**mySampleDesc).desc.dataFormat        = theDestInfo->format;
  1084.     (**mySampleDesc).desc.resvd1            = 0;
  1085.     (**mySampleDesc).desc.resvd2            = 0;
  1086.     (**mySampleDesc).desc.dataRefIndex        = 1;
  1087.     (**mySampleDesc).desc.version            = 1;
  1088.     (**mySampleDesc).desc.revlevel            = 0;
  1089.     (**mySampleDesc).desc.vendor            = 0;
  1090.     (**mySampleDesc).desc.numChannels        = theDestInfo->numChannels;
  1091.     (**mySampleDesc).desc.sampleSize        = theDestInfo->sampleSize;
  1092.     (**mySampleDesc).desc.compressionID        = 0;
  1093.     (**mySampleDesc).desc.packetSize        = 0;
  1094.     (**mySampleDesc).desc.sampleRate        = theDestInfo->sampleRate;
  1095.     (**mySampleDesc).samplesPerPacket        = theDestCompInfo->samplesPerPacket;
  1096.     (**mySampleDesc).bytesPerPacket            = theDestCompInfo->bytesPerPacket;
  1097.     (**mySampleDesc).bytesPerFrame            = theDestCompInfo->bytesPerFrame;
  1098.     (**mySampleDesc).bytesPerSample            = theDestCompInfo->bytesPerSample;
  1099.  
  1100.     // not all compression formats have compression params, so we need to add a
  1101.     // sound description extension only for those that do
  1102.     if (theDestCompParamsHandle != NULL) {
  1103.         myErr = AddSoundDescriptionExtension((SoundDescriptionHandle)mySampleDesc, theDestCompParamsHandle, siDecompressionParams);
  1104.         FailIf(myErr != noErr, MediaErr);
  1105.     }
  1106.  
  1107.     //////////
  1108.     //                
  1109.     // add samples to the media
  1110.     //                
  1111.     //////////
  1112.  
  1113.     myErr = AddMediaSample(    myMedia,
  1114.                             theDestAudioData,
  1115.                             0,
  1116.                             theDestInfo->sampleCount * theDestCompInfo->bytesPerFrame,
  1117.                             1,
  1118.                             (SampleDescriptionHandle)mySampleDesc,
  1119.                             theDestInfo->sampleCount * theDestCompInfo->samplesPerPacket,
  1120.                             0,
  1121.                             NULL);
  1122.     FailIf(myErr != noErr, MediaErr);
  1123.  
  1124.     myErr = EndMediaEdits(myMedia);
  1125.     FailIf(myErr != noErr, MediaErr);
  1126.  
  1127.     //////////
  1128.     //                
  1129.     // insert the media into the track
  1130.     //                
  1131.     //////////
  1132.  
  1133.     myErr = InsertMediaIntoTrack(theTrack, 0, 0, GetMediaDuration(myMedia), fixed1);
  1134.     FailIf(myErr != noErr, MediaErr);
  1135.  
  1136. MediaErr:
  1137.     if (mySampleDesc != NULL)    
  1138.         DisposeHandle((Handle)mySampleDesc);
  1139.         
  1140. Exit:
  1141.     return(myErr);
  1142. }
  1143.  
  1144.  
  1145. //////////
  1146. //
  1147. // AudConv_SFGetDialogHook
  1148. // Hook function for the get file dialog box.
  1149. //
  1150. //////////
  1151.  
  1152. PASCAL_RTN short AudConv_SFGetDialogHook (short theItem, DialogPtr theDialog, void *theOutputAIFF)
  1153. {
  1154.     ControlHandle            myAIFFControl;
  1155.     ControlHandle            myMovieControl;
  1156.     Rect                    myRect;
  1157.     short                    myKind;
  1158.     
  1159.     // make sure we've got a real dialog
  1160.     if (theDialog == NULL)
  1161.         return(theItem);
  1162.  
  1163.     GetDialogItem(theDialog, kOutputAIFFButton, &myKind, (Handle *)&myAIFFControl, &myRect);
  1164.     GetDialogItem(theDialog, kOutputMovieButton, &myKind, (Handle *)&myMovieControl, &myRect);
  1165.  
  1166.     switch (theItem) {
  1167.         case sfHookFirstCall:
  1168.             if (*(Boolean *)theOutputAIFF)
  1169.                 SetControlValue(myAIFFControl, kControlRadioButtonCheckedValue);
  1170.             else
  1171.                 SetControlValue(myMovieControl, kControlRadioButtonUncheckedValue);
  1172.             break;
  1173.  
  1174.         case kOutputAIFFButton:
  1175.             SetControlValue(myAIFFControl, kControlRadioButtonCheckedValue);
  1176.             SetControlValue(myMovieControl, kControlRadioButtonUncheckedValue);
  1177.             *(Boolean *)theOutputAIFF = true;
  1178.             break;
  1179.  
  1180.         case kOutputMovieButton:
  1181.             SetControlValue(myAIFFControl, kControlRadioButtonUncheckedValue);
  1182.             SetControlValue(myMovieControl, kControlRadioButtonCheckedValue);
  1183.             *(Boolean *)theOutputAIFF = false;
  1184.             break;
  1185.     }
  1186.     
  1187.     return(theItem);
  1188. }
  1189.  
  1190.  
  1191.